package com.cayte.location;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.util.EntityUtils;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import android.content.Context;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.wifi.ScanResult;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.telephony.NeighboringCellInfo;
import android.telephony.TelephonyManager;
import android.telephony.cdma.CdmaCellLocation;
import android.telephony.gsm.GsmCellLocation;
import android.util.Xml.Encoding;

public class CayteLocationHelper {
	private Context con;
	public LocationManager locationManager;
	public List<LbsListener> listeners;
	public List<String> providers;
	private long minTime;
	private DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");

	public void setMinTime(long minTime) {
		if (minTime < 3000)
			minTime = 3000;
		this.minTime = minTime;
	}

	public CayteLocationHelper(Context con) {
		this.con = con;
		init();
	}

	private void init() {
		setMinTime(5000);
		locationManager = (LocationManager) con
				.getSystemService(Context.LOCATION_SERVICE);
		initProviders();
		initListeners();
	}

	private void initProviders() {
		providers = new ArrayList<String>();
		for (String p : locationManager.getProviders(true)) {
			if ((p.equals(LocationManager.GPS_PROVIDER) || p
					.equals(LocationManager.NETWORK_PROVIDER))
					&& !providers.contains(p))
				providers.add(p);
		}
	}

	private void initListeners() {
		listeners = new ArrayList<LbsListener>();
		for (String p : providers) {
			if (!containsListener(p)) {
				listeners.add(new LbsListener(p));
			}
		}
	}

	private boolean containsListener(String provider) {
		for (LbsListener l : listeners) {
			if (l.getProvider().equals(provider))
				return true;
		}
		return false;
	}

	private void removeListener(String provider) {
		for (LbsListener l : listeners) {
			if (l.getProvider().equals(provider)) {
				listeners.remove(l);
			}
		}
	}

	private class LbsListener implements LocationListener {
		private String provider;

		public String getProvider() {
			return provider;
		}

		public LbsListener(String provider) {
			this.provider = provider;
		}

		@Override
		public void onLocationChanged(Location location) {
			CayteLocationHelper.this.location = location;
		}

		@Override
		public void onProviderDisabled(String provider) {
			if ((provider.equals(LocationManager.GPS_PROVIDER) || provider
					.equals(LocationManager.NETWORK_PROVIDER))) {
				providers.remove(provider);
				removeListener(provider);
				removeUpdates(provider);
			}
		}

		@Override
		public void onProviderEnabled(String provider) {
			if ((provider.equals(LocationManager.GPS_PROVIDER) || provider
					.equals(LocationManager.NETWORK_PROVIDER))
					&& !providers.contains(provider)) {
				providers.add(provider);
				if (!containsListener(provider)) {
					listeners.add(new LbsListener(provider));
					requestLocationUpdates(provider);
				}
			}
		}

		@Override
		public void onStatusChanged(String provider, int status, Bundle extras) {
		}

	}

	public void requestAllLocationUpdates() {
		for (LbsListener l : listeners) {
			locationManager.requestLocationUpdates(l.getProvider(), minTime,
					10, l);
		}
	}

	public void removeAllUpdates() {
		for (LbsListener l : listeners) {
			locationManager.removeUpdates(l);
		}
	}

	public void requestLocationUpdates(String provider) {
		for (LbsListener l : listeners) {
			if (l.getProvider().equals(provider))
				locationManager
						.requestLocationUpdates(provider, minTime, 10, l);
		}

	}

	public void removeUpdates(String provider) {
		for (LbsListener l : listeners) {
			if (l.getProvider().equals(provider))
				locationManager.removeUpdates(l);
		}
	}

	public void sleep() {
		long sleepTime = minTime;
		int count = (int) (minTime / 1000);
		for (int i = 0; i < count; i++) {
			if (sleepTime != minTime) {
				return;
			}
			try {
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}

	private WifiManager wifiManager;
	private TelephonyManager telephonyManager;
	private ConnectivityManager connectivityManager;

	private int getConnectedType() {
		if (connectivityManager == null)
			connectivityManager = (ConnectivityManager) con
					.getSystemService(Context.CONNECTIVITY_SERVICE);
		NetworkInfo mNetworkInfo = connectivityManager.getActiveNetworkInfo();
		if (mNetworkInfo != null && mNetworkInfo.isAvailable()) {
			return mNetworkInfo.getType();
		}
		return -1;
	}

	private Location location = null;

	public CayteLocation getLocation() {
		// TODO Auto-generated method stub
		CayteLocation loc = new CayteLocation();
		if (location == null) {
			loc.state = CayteLocationState.NULL;
			return loc;
		}
		try {
			loc.provider = location.getProvider();
			loc.time = dateFormat.format(new Date(location.getTime()));
			loc.accuracy = location.getAccuracy();
			loc.latitude = location.getLatitude();
			loc.longitude = location.getLongitude();
			loc.state = CayteLocationState.SUCCESS;
			return loc;
		} catch (Exception e) {
			// TODO: handle exception
			loc.state = CayteLocationState.FAIL_UNKNOW;
			return loc;
		}
	}

	/**
	 * 从网络获取位置,基站或者WIFI
	 * 
	 * @return CayteLocation
	 */
	public CayteLocation getNetworkLocation() {
		// TODO Auto-generated method stub
		return getNetworkLocation(null);
	}

	/**
	 * 从网络获取位置,基站或者WIFI
	 * 
	 * @param lastLoc
	 *            上一次定位位置
	 * @return CayteLocation
	 */
	public CayteLocation getNetworkLocation(CayteLocation lastLoc) {
		// TODO Auto-generated method stub
		try {
			int type = getConnectedType();
			if (type == ConnectivityManager.TYPE_WIFI) {
				return getNetworkLocation(lastLoc, type);
			} else if (type == ConnectivityManager.TYPE_MOBILE) {
				return getNetworkLocation(lastLoc, type);
			} else {
				CayteLocation loc = new CayteLocation();
				loc.state = CayteLocationState.FAIL_4_NETWORK;
				return loc;
			}
		} catch (Exception e) {
			// TODO: handle exception
			CayteLocation loc = new CayteLocation();
			loc.state = CayteLocationState.FAIL_UNKNOW;
			return loc;
		}
	}

	private CayteLocation getNetworkLocation(CayteLocation lastLoc, int type) {
		// TODO Auto-generated method stub
		ArrayList<WifiObj> wifis = getWifisInfo();
		ArrayList<CellObj> cells = getCellsInfo();
		String radio_type = "";
		if (type == ConnectivityManager.TYPE_WIFI)
			radio_type = "wifi";
		else if (type == ConnectivityManager.TYPE_MOBILE) {
			radio_type = getGsmOrCdma();
		}
		String location = getLastLocString(lastLoc);
		String jsonContent = getJsonString(radio_type, location, cells, wifis);
		return getLocation(radio_type, jsonContent);
	}

	private String getLastLocString(CayteLocation lastLoc) {
		// TODO Auto-generated method stub
		JSONObject location = new JSONObject();
		if (lastLoc != null) {
			try {
				location.put("latitude", lastLoc.latitude);
				location.put("longitude", lastLoc.longitude);
				location.put("accuracy", lastLoc.accuracy);
			} catch (JSONException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return location.toString();
	}

	private ArrayList<WifiObj> getWifisInfo() {
		// TODO Auto-generated method stub
		if (wifiManager == null)
			wifiManager = (WifiManager) con
					.getSystemService(Context.WIFI_SERVICE);

		ArrayList<WifiObj> wifis = new ArrayList<WifiObj>();
		WifiObj w;
		String bssid = wifiManager.getConnectionInfo().getBSSID();
		String ssid = wifiManager.getConnectionInfo().getSSID();
		int ss = wifiManager.getConnectionInfo().getRssi();
		w = new WifiObj(bssid, ssid, ss);
		wifis.add(w);
		if (wifiManager.getScanResults() != null)
			for (ScanResult sr : wifiManager.getScanResults()) {
				if (bssid.equals(sr.BSSID))
					continue;
				w = new WifiObj(sr.BSSID, sr.BSSID, sr.level);
				wifis.add(w);
			}
		return wifis;
	}

	private class WifiObj {
		public String macAddress;
		public String macName;
		public int ss;

		public WifiObj(String macAddress, String macName, int ss) {
			// TODO Auto-generated constructor stub
			this.macAddress = macAddress;
			this.macName = macName;
			this.ss = ss;
		}
	}

	private String getGsmOrCdma() {
		// TODO Auto-generated method stub
		if (telephonyManager == null)
			telephonyManager = (TelephonyManager) con
					.getSystemService(Context.TELEPHONY_SERVICE);
		int type = telephonyManager.getNetworkType();
		if (type == TelephonyManager.NETWORK_TYPE_GPRS // GSM网
				|| type == TelephonyManager.NETWORK_TYPE_EDGE
				|| type == TelephonyManager.NETWORK_TYPE_HSDPA) {
			return "gsm";
		} else if (type == TelephonyManager.NETWORK_TYPE_CDMA // 电信cdma网
				|| type == TelephonyManager.NETWORK_TYPE_1xRTT
				|| type == TelephonyManager.NETWORK_TYPE_EVDO_0
				|| type == TelephonyManager.NETWORK_TYPE_EVDO_A) {
			return "cdma";
		} else {
			return "";
		}
	}

	private ArrayList<CellObj> getCellsInfo() {

		if (telephonyManager == null)
			telephonyManager = (TelephonyManager) con
					.getSystemService(Context.TELEPHONY_SERVICE);

		ArrayList<CellObj> cells = new ArrayList<CellObj>();
		CellObj currentCell = new CellObj();
		int type = telephonyManager.getNetworkType();
		try {
			if (type == TelephonyManager.NETWORK_TYPE_GPRS // GSM网
					|| type == TelephonyManager.NETWORK_TYPE_EDGE
					|| type == TelephonyManager.NETWORK_TYPE_HSDPA) {
				GsmCellLocation gsm = ((GsmCellLocation) telephonyManager
						.getCellLocation());
				if (gsm == null) {
					return cells;
				}

				int lac = gsm.getLac();
				int mcc = Integer.valueOf(telephonyManager.getNetworkOperator()
						.substring(0, 3));
				String mnc = telephonyManager.getNetworkOperator().substring(3,
						5);
				// int cid = gsm.getCid();

				currentCell.cellId = gsm.getCid();
				currentCell.mcc = mcc;
				currentCell.mnc = mnc;
				currentCell.lac = lac;
				currentCell.ss = 0;

				cells.add(currentCell);

				// 获得邻近基站信息
				List<NeighboringCellInfo> list = telephonyManager
						.getNeighboringCellInfo();
				int size = list.size();
				for (int i = 0; i < size; i++) {
					CellObj info = new CellObj();
					info.cellId = list.get(i).getCid();
					info.mcc = mcc;
					info.mnc = mnc;
					info.lac = lac;
					info.ss = list.get(i).getRssi();

					cells.add(info);
				}

			} else if (type == TelephonyManager.NETWORK_TYPE_CDMA // 电信cdma网
					|| type == TelephonyManager.NETWORK_TYPE_1xRTT
					|| type == TelephonyManager.NETWORK_TYPE_EVDO_0
					|| type == TelephonyManager.NETWORK_TYPE_EVDO_A) {

				CdmaCellLocation cdma = (CdmaCellLocation) telephonyManager
						.getCellLocation();
				if (cdma == null) {
					return cells;
				}

				int lac = cdma.getNetworkId();
				int mcc = Integer.valueOf(telephonyManager.getNetworkOperator()
						.substring(0, 3));
				String mnc = String.valueOf(cdma.getSystemId());
				int cid = cdma.getBaseStationId();

				currentCell.cellId = cid;
				currentCell.mcc = mcc;
				currentCell.mnc = mnc;
				currentCell.lac = lac;
				currentCell.ss = 0;

				cells.add(currentCell);

				// 获得邻近基站信息
				List<NeighboringCellInfo> list = telephonyManager
						.getNeighboringCellInfo();
				int size = list.size();
				for (int i = 0; i < size; i++) {

					CellObj info = new CellObj();
					info.cellId = list.get(i).getCid();
					info.mcc = mcc;
					info.mnc = mnc;
					info.lac = lac;
					info.ss = list.get(i).getRssi();

					cells.add(info);
				}
			}
		} catch (Exception e) {
			// TODO: handle exception
		}

		return cells;

	}

	private class CellObj {
		public int cellId;
		public int lac;
		public int mcc;
		public String mnc;
		public int ss;

		public CellObj() {
			// TODO Auto-generated constructor stub
		}

	}

	private String getJsonString(String radio_type, String location,
			ArrayList<CellObj> cells, ArrayList<WifiObj> wifis) {
		// TODO Auto-generated method stub
		JSONObject holder = new JSONObject();

		try {
			holder.put("version", "1.1.0");
			holder.put("host", "api.weibo.com/2/location/mobile/get_location");
			holder.put("radio_type", radio_type);
			holder.put("request_address", true);
			holder.put("decode_pos", false);
			holder.put("location", location);

			JSONArray arr = null;
			JSONObject obj = null;

			arr = new JSONArray();
			for (CellObj cell : cells) {
				obj = new JSONObject();
				obj.put("cell_id", cell.cellId);
				obj.put("location_area_code", cell.lac);
				obj.put("mobile_country_code", cell.mcc);
				obj.put("mobile_network_code", cell.mnc);
				obj.put("signal_strength", cell.ss);
				arr.put(obj);
			}
			holder.put("cell_towers", arr);

			arr = new JSONArray();
			for (WifiObj wifi : wifis) {
				obj = new JSONObject();
				obj.put("mac_address", wifi.macAddress);
				obj.put("mac_name", wifi.macName);
				obj.put("signal_strength", wifi.ss);
				arr.put(obj);
			}
			holder.put("wifi_towers", arr);

		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return holder.toString();
	}

	private CayteLocation getLocation(String provider, String content) {
		CayteLocation loc = new CayteLocation();
		if (content == null) {
			loc.state = CayteLocationState.FAIL_UNKNOW;
			return loc;
		}

		DefaultHttpClient client = new DefaultHttpClient();
		HttpPost post = new HttpPost(
				"https://api.weibo.com/2/location/mobile/get_location.json?source=212636772");
		post.setHeader("Content-type", "application/json");
		post.setHeader("Authorization",
				"Basic bzIyMDM0NjVAcnRydHIuY29tOjFxMnczZTRy");
		try {
			StringEntity se = new StringEntity(content);
			post.setEntity(se);
			HttpResponse resp = client.execute(post);
			int state = resp.getStatusLine().getStatusCode();

			if (state == HttpStatus.SC_OK) {
				HttpEntity entity = resp.getEntity();
				if (entity != null) {
					String res = EntityUtils.toString(entity);

					JSONObject data = new JSONObject(res);
					data = (JSONObject) data.get("location");
					loc.provider = android.location.LocationManager.NETWORK_PROVIDER
							+ " - " + provider;
					loc.latitude = data.getDouble("latitude");
					loc.longitude = data.getDouble("longitude");
					loc.accuracy = Float.parseFloat(data.get("accuracy")
							.toString());
					loc.time = dateFormat.format(new Date());

					String address = "";
					if (data.has("address")) {
						JSONObject addressJson = data.getJSONObject("address");
						if (!isNull(addressJson.getString("city_name")))
							address += new String(addressJson.getString(
									"city_name").getBytes(),
									Encoding.UTF_8.toString())
									+ " ";
						if (!isNull(addressJson.getString("province_name")))
							address += new String(addressJson.getString(
									"province_name").getBytes(),
									Encoding.UTF_8.toString())
									+ " ";
						if (!isNull(addressJson.getString("district")))
							address += new String(addressJson.getString(
									"district").getBytes(),
									Encoding.UTF_8.toString())
									+ " ";
						if (!isNull(addressJson.getString("address")))
							address += new String(addressJson.getString(
									"address").getBytes(),
									Encoding.UTF_8.toString())
									+ " ";
						loc.address = address;
					}

					loc.state = CayteLocationState.SUCCESS;
					return loc;
				} else {
					loc.state = CayteLocationState.FAIL_UNKNOW;
					return loc;
				}
			} else if (state == 0) {
				loc.state = CayteLocationState.FAIL_4_NETWORK;
				return loc;
			} else {
				loc.state = CayteLocationState.FAIL_UNKNOW;
				return loc;
			}

		} catch (Exception e) {
			loc.state = CayteLocationState.FAIL_UNKNOW;
			return loc;
		}
	}

	private boolean isNull(String str) {
		// TODO Auto-generated method stub
		if (str == null)
			return true;
		if (str.trim().equals("") || str.trim().equals("null"))
			return true;
		return false;
	}
}
